/*
 * Copyright (C) 2021 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *    1. Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the
 *       distribution.
 *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
 *       its contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <string.h>
#include "os.h"
#include "common/cmd/cmd.h"
#include "wifi_hotspot.h"
#include "cmd_hm_net_sta.h"

#define cmd_nitems(a) (sizeof((a)) / sizeof((a)[0]))

#define WIFI_HOSTAP_SSID "WIFI_DEVICE_AP_TEST"
#define WIFI_HOSTAP_PSK "12345678"

static WifiEvent ap_event;

HotspotConfig ap_config = { .ssid = WIFI_HOSTAP_SSID,
			    .securityType = WIFI_SEC_TYPE_PSK,
			    .band = HOTSPOT_BAND_TYPE_2G,
			    .channelNum = 1,
			    .preSharedKey = WIFI_HOSTAP_PSK };

static void printf_mac_address(unsigned char *mac_address)
{
	for (int i = 0; i < WIFI_MAC_LEN - 1; i++) {
		printf("%02X:", mac_address[i]);
	}
	printf("%02X\n", mac_address[WIFI_MAC_LEN - 1]);
}

static void Ap_started_deal(int state)
{
	if (state == WIFI_STATE_AVALIABLE) {
		printf("=====> Callback: ap is started\n");
	}
}

static void Ap_sta_join(StationInfo *info)
{
	printf("=====> Callback: station join, the mac address: ");
	printf_mac_address(info->macAddress);
}

static void Ap_sta_leave(StationInfo *info)
{
	printf("=====> Callback: station leave, the mac address: ");
	printf_mac_address(info->macAddress);
}

WifiEvent *AP_CallBack_Register(void)
{
	return &ap_event;
}

static enum cmd_status cmd_net_ap_config_exec(char *cmd)
{
	char *argv[3];
	unsigned int cnt = 0;
	unsigned int channel = 0;
	uint8_t *ssid = NULL;
	uint8_t *psk = NULL;

	cnt = cmd_parse_argv(cmd, argv, cmd_nitems(argv));
	if (cnt == 2) {
		if (strlen(argv[1]) >= (WIFI_MAX_SSID_LEN + strlen("ssid="))) {
			return CMD_STATUS_INVALID_ARG;
		}
		ssid = (argv[1] + strlen("ssid="));
		if (cmd_sscanf(argv[0], "ch=%d", &channel) == 0){
			return CMD_STATUS_INVALID_ARG;
		}
		strncpy(ap_config.ssid, ssid, WIFI_MAX_SSID_LEN);
		ap_config.securityType = WIFI_SEC_TYPE_OPEN,
		ap_config.band = HOTSPOT_BAND_TYPE_2G,
		ap_config.channelNum = channel,
		memset(ap_config.preSharedKey, 0, WIFI_MAX_KEY_LEN);
	} else if (cnt == 3) {
		if ((strlen(argv[1]) >=
		     (WIFI_MAX_SSID_LEN + strlen("ssid="))) ||
		    (strlen(argv[2]) >= (WIFI_MAX_KEY_LEN + strlen("psk=")))) {
			return CMD_STATUS_INVALID_ARG;
		}
		ssid = (argv[1] + strlen("ssid="));
		psk = (argv[2] + strlen("psk="));
		printf("ssid = %s\n",ssid);
		printf("psk = %s\n",psk);
		if (cmd_sscanf(argv[0], "ch=%d", &channel) == 0){
			return CMD_STATUS_INVALID_ARG;
		}
		strncpy(ap_config.ssid, ssid, WIFI_MAX_SSID_LEN);
		ap_config.securityType = WIFI_SEC_TYPE_PSK;
		ap_config.band = HOTSPOT_BAND_TYPE_2G;
		ap_config.channelNum = channel,
		strncpy(ap_config.preSharedKey, psk, WIFI_MAX_KEY_LEN);
	}

	if (WIFI_SUCCESS != SetHotspotConfig(&ap_config)) {
		printf("Error: SetHotspotConfig Fail\n");
		return CMD_STATUS_FAIL;
	}
	printf("SetHotspotConfig Success\n");
	return CMD_STATUS_OK;
}

static enum cmd_status cmd_net_ap_enable_exec(char *cmd)
{
	WifiErrorCode res = ERROR_WIFI_UNKNOWN;

	if (IsWifiActive() == WIFI_STA_ACTIVE) {
		if (DisableWifi() != WIFI_SUCCESS) {
			return ERROR_WIFI_BUSY;
		}
		UnRegisterWifiEvent(STA_CallBack_Register());
	}

	res = EnableHotspot();
	if (res != WIFI_SUCCESS) {
		printf("[AP_error]:ErrorNum = %d\n", res);
		return CMD_STATUS_FAIL;
	}

	ap_event.OnWifiConnectionChanged = NULL;
	ap_event.OnWifiScanStateChanged = NULL;
	ap_event.OnHotspotStateChanged = Ap_started_deal;
	ap_event.OnHotspotStaJoin = Ap_sta_join;
	ap_event.OnHotspotStaLeave = Ap_sta_leave;

	if (WIFI_SUCCESS != RegisterWifiEvent(&ap_event)) {
		printf("Error: RegisterWifiEvent fail\n");
		return CMD_STATUS_FAIL;
	}
	printf("RegisterWifiEvent Success\n");

	printf("EnableHotspot Success, Use your phone to connect it:\n");

	return CMD_STATUS_OK;
}

static enum cmd_status cmd_net_ap_disable_exec(char *cmd)
{
	if (WIFI_SUCCESS != DisableHotspot()) {
		printf("Error: DisableHotspot Fail\n");
		return CMD_STATUS_FAIL;
	}

	UnRegisterWifiEvent(&ap_event);
	printf("DisableHotspot Success\n");

	return CMD_STATUS_OK;
}

static enum cmd_status cmd_net_ap_get_exec(char *cmd)
{
	int ret;

	if (cmd_strcmp(cmd, "state") == 0) {
		if (WIFI_HOTSPOT_ACTIVE == IsHotspotActive()) {
			printf("Wifi is active\n");
		} else {
			printf("Wifi isn't active\n");
		}
	} else if (cmd_strcmp(cmd, "config") == 0) {
		HotspotConfig result;
		if (WIFI_SUCCESS != GetHotspotConfig(&result)) {
			return CMD_STATUS_FAIL;
		} else {
			printf("GetHotspotConfig success\n");
			printf("ssid: %s \n", result.ssid);
			printf("psk: %s \n", result.preSharedKey);
			printf("band: %d \n", result.band);
			printf("channel: %d \n", result.channelNum);
			printf("securutyType: %d \n", result.securityType);
		}
	} else if (cmd_strcmp(cmd, "station_list") == 0) {
		int sec = 0;
		unsigned int size = 10;
		StationInfo *result = malloc(sizeof(StationInfo) * size);

		while (sec++ < 30) {
			OS_Sleep(1);

			if (sec % 10 != 0) {
				continue;
			}

			printf("\n=============== GetStationList ===================\n");
			size = 10;
			if (WIFI_SUCCESS != GetStationList(result, &size)) {
				return CMD_STATUS_FAIL;
			}

			for (int i = 0; i < size; i++) {
				printf("%d. ", i + 1);
				printf_mac_address(result[i].macAddress);
			}
			printf("=====================================================\n");
		}

		free(result);
	} else if (cmd_strcmp(cmd, "channel") == 0) {
		int channel;

		channel = GetHotspotChannel();
		if (channel < 0) {
			printf("HostAP GetHotspotChannel failed\r\n");
		} else {
			printf("HostAP channel = %d\r\n", channel);
		}
	} else if (cmd_strcmp(cmd, "name") == 0) {
		HotspotConfig result;

		ret = GetHotspotInterfaceName(&result);
		if (ret < 0) {
			printf("HostAP GetHotspotInterfaceName failed\r\n");
			return CMD_STATUS_FAIL;
		} else {
			printf("HostAP name: %s\r\n", result.ssid);
		}
	} else {
	}

	return CMD_STATUS_OK;
}

static enum cmd_status cmd_net_ap_set_exec(char *cmd)
{
	if (cmd_strncmp(cmd, "TxPowerInfo", 11) == 0) {
		int power;
		char *end;

		power = cmd_strtol(cmd + 12, &end, 0);
		if (*end) {
			return CMD_STATUS_FAIL;
		}
		if ((power < -1) || (power > 20)) {
			return CMD_STATUS_INVALID_ARG;
		}

		if (WIFI_SUCCESS != AddTxPowerInfo(power)) {
			printf("HostAP AddTxPowerInfo failed\r\n");
		} else {
			printf("HostAP AddTxPowerInfo power=%d susscess\r\n",
			       power);
		}
	}

	return CMD_STATUS_OK;
}

static const struct cmd_data g_net_ap_cmds[] = {
	{ "config", cmd_net_ap_config_exec, CMD_DESC("config command") },
	{ "enable", cmd_net_ap_enable_exec, CMD_DESC("enable command") },
	{ "disable", cmd_net_ap_disable_exec, CMD_DESC("disable command") },
	{ "get", cmd_net_ap_get_exec, CMD_DESC("get command") },
	{ "set", cmd_net_ap_set_exec, CMD_DESC("set command") },
};

enum cmd_status cmd_hm_net_ap_exec(char *cmd)
{
	return cmd_exec(cmd, g_net_ap_cmds, cmd_nitems(g_net_ap_cmds));
}
